#include "header.h"
#include "ConfigFile.h"
#include "IPToolkit.h"
#include "Log.h"
#include "Debug.h"
#include "SDRS.h"
#include "CharFeature.h"
#include "ml/SampleSpace.h"
#include "ml/MPLSH.h"
#include "FontLib.h"
#include "ml/KNN.h"
#include "CharExtracter.h"
#include "OCR.h"
#include "Config.h"

#include <cstdlib>
#include <sstream>
#include <iostream>
#include <fstream>
#include <iomanip>
#include <cstdio>
#include <cassert>

#if 0

#include <cxcore.h>
#include <cv.h>
#include <highgui.h>


using namespace std;
 
#define TRAIN 3

#if TRAIN == 3

// maintain the max feature lib
void findXYWH(uchar* data, int* x, int* y, int* width, int* height) {
    int normSize = CharFeature::s_NORM_SIZE;
    bool needBreak = false;

    for (int i = 0; i < normSize; i++) {
        for (int j = 0; j < normSize; j++) {
            if (*(data + normSize * i + j) == CHAR) {
                *y = i;

                needBreak = true;
                break;
            }
        }

        if (needBreak) {
            break;
        }
    }

    needBreak = false;
    for (int i = normSize - 1; i >= 0; i--) {
        for (int j = 0; j < normSize; j++) {
            if (*(data + normSize * i + j) == CHAR) {
                *height = i - *y + 1;

                needBreak = true;
                break;
            }
        }

        if (needBreak) {
            break;
        }
    }

    needBreak = false;
    for (int j = 0; j < normSize; j++) {
        for (int i = 0; i < normSize; i++) {
            if (*(data + normSize * i + j) == CHAR) {
                *x = j;

                needBreak = true;
                break;
            }
        }

        if (needBreak) {
            break;
        }
    }

    needBreak = false;
    for (int j = normSize - 1; j >= 0; j--) {
        for (int i = 0; i < normSize; i++) {
            if (*(data + normSize * i + j) == CHAR) {
                *width = j - *x + 1;

                needBreak = true;
                break;
            }
        }

        if (needBreak) {
            break;
        }
    }
}

void reduceResolution(IplImage* image, int scale, int type = CV_INTER_NN, int threshold = 128) {
    IplImage* temp;

    switch (scale) {
        case 1:
            temp = cvCreateImage(cvSize(48, 48), 8, 1);
            break;

        case 2:
            temp = cvCreateImage(cvSize(32, 32), 8, 1);
            break;

        case 3:
            temp = cvCreateImage(cvSize(24, 24), 8, 1);
            break;

        case 4:
            temp = cvCreateImage(cvSize(16, 16), 8, 1);
            break;

        default:
            assert(false);

    }

    cvResize(image, temp);
    cvResize(temp, image, type);
    Debug::binarize(image, threshold);

    cvReleaseImage(&temp);
}

void distorteAndNorm(uchar** samples, const uchar* origin) {
    int normSize = CharFeature::s_NORM_SIZE, count = 0;
    int x, y, width, height;
    uchar* data = new uchar[normSize * normSize], *prototype = new uchar[normSize * normSize];

    memcpy(prototype, origin, normSize * normSize);

    IplImage* src = cvCreateImageHeader(cvSize(normSize, normSize), 8, 1);
    IplImage* dst = cvCreateImageHeader(cvSize(normSize, normSize), 8, 1);
    cvSetData(src, prototype, normSize);
    cvSetData(dst, data, normSize);

    IplImage* temp = cvCreateImage(cvSize(normSize, normSize), 8, 1);

    /*
    CV_SHAPE_RECT, a rectangular element;
    CV_SHAPE_CROSS, a cross-shaped element;
    CV_SHAPE_ELLIPSE, an elliptic element;
     */
    IplConvKernel* element = cvCreateStructuringElementEx(2, 2, 0, 0, CV_SHAPE_ELLIPSE, 0);

    // display the source image
    // DebugToolkit::displayImage(src);

    /************************************************************************/
    /* the first group                                                      */
    /************************************************************************/
    cvErode(src, dst, element, 1);
    cvCopyImage(dst, temp);

    // normal
    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
     Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 1, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 1);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 2, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 2);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    /************************************************************************/
    /* the second group                                                     */
    /************************************************************************/
    cvDilate(src, dst, element, 1);
    cvCopyImage(dst, temp);

    // normal
    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 1, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 1);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 2, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 2);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 3, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 3);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    /*// scale 4, CV_INTER_LINEAR, threshold 110
    cvCopyImage(temp, dst);
    reduceResolution(dst, 4, CV_INTER_LINEAR, 110);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showGreyImageData(samples[count - 1], normSize, normSize);
#endif*/

    /************************************************************************/
    /* the third group                                                      */
    /************************************************************************/
    cvDilate(src, dst);
    cvCopyImage(dst, temp);

    // normal
    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 2, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 2);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 3, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 3);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 4, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 4, CV_INTER_LINEAR);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    /************************************************************************/
    /* the fourth group                                                     */
    /************************************************************************/
    cvCopyImage(src, temp);

    // normal
    findXYWH(prototype, &x, &y, &width, &height);
    OCR::normalize(samples[count++], prototype, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 1, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 1);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 2, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 2);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    // scale 3, CV_INTER_NN, threshold 128
    cvCopyImage(temp, dst);
    reduceResolution(dst, 3, CV_INTER_LINEAR, 100);

    findXYWH(data, &x, &y, &width, &height);
    OCR::normalize(samples[count++], data, normSize, x, y, width, height);

#ifdef SHOW_DISTORTION
    Debug::showImageData(samples[count - 1], normSize, normSize);
#endif

    cvReleaseImageHeader(&src);
    cvReleaseImageHeader(&dst);
    cvReleaseImage(&temp);

    cvReleaseStructuringElement(&element);

    delete[] data;
    delete[] prototype;

    assert(count == Config::DISTORTE_NUM);
}

void extractFeature(FontLib *fontLib, const char *featurePath) {
    const int sampleSize = Config::DISTORTE_NUM;

    const int charCount = fontLib->size();

    const int normSize = CharFeature::s_NORM_SIZE;
    const int featureSize = CharFeature::s_FEATURE_SIZE;

    uchar **imageData = new uchar*[sampleSize];
    for (int i = 0; i < sampleSize; i++) {
        imageData[i] = new uchar[normSize * normSize];
    }

    CharFeature extracter = CharFeature::getInstance();
    SampleSpace ss(featureSize);

    vector<Sample *> sampleList;
    Sample *s = NULL;
    cout << "sample generating process:" << endl;
    for (int i = 0; i < charCount; i++) {
        distorteAndNorm(imageData, fontLib->at(i)->getImageData());

        for (int k = 0; k < sampleSize; k++) {
            s = ss.newSample();
            s->setClass(fontLib->at(i)->getValue());

            extracter.extractScaledFeature(s->getFeature(), imageData[k]);
            sampleList.push_back(s);
        }

        if (i % 100 == 0) {
            cout << setprecision(2) << i * 100 * 1.0 / charCount << "% finished" << endl;
        }
    }
    cout << "100% finished" << endl;

    ofstream fout(featurePath);
    ss.storeSample2Stream(fout, sampleList);
    fout.close();

    for (int i = 0; i < sampleSize; i++) {
        delete[] imageData[i];
    }
    delete[] imageData;

    ss.deleteSampleArray(sampleList);
}

int main() {
    Config &config = Config::getInstance();
    
    FontLib *fontLib = NULL;

    string path = config.getDataDirPath();;
    path += "/max/heiti_64";
    FILE *file = fopen(path.c_str(), "rb");
    assert(file != NULL);
    fontLib = FontLib::parseLibFile(file);
    fclose(file);
    path += "_feature";
    extractFeature(fontLib, path.c_str());
    delete fontLib;

    /*
    path = config.getDataDirPath();;
    path += "/max/songti_64";
    file = fopen(path.c_str(), "rb");
    assert(file != NULL);
    fontLib = FontLib::parseLibFile(file);
    fclose(file);
    path += "_feature";
    extractFeature(fontLib, path.c_str());
    delete fontLib;

    path = config.getDataDirPath();;
    path += "/max/heiti_16";
    file = fopen(path.c_str(), "rb");
    assert(file != NULL);
    fontLib = FontLib::parseLibFile(file);
    fclose(file);
    path += "_feature";
    extractFeature(fontLib, path.c_str());
    delete fontLib;

    path = config.getDataDirPath();;
    path += "/max/songti_16";
    file = fopen(path.c_str(), "rb");
    assert(file != NULL);
    fontLib = FontLib::parseLibFile(file);
    fclose(file);
    path += "_feature";
    extractFeature(fontLib, path.c_str());
    delete fontLib; */

    return 0;
}

#elif TRAIN == 2

// to recognize a list of binarized image
void func(const char *filename) {
    IplImage *img = cvLoadImage(filename, 0);
    if(img == NULL) {
        cout << "cannot find file: " << filename << endl;
        return;
    }

    uchar *imageData = (uchar *)(img->imageData);
    int width = img->width;
    int height = img->height;
    int widthStep = img->widthStep;

    uchar *greyData = new uchar[width*height];
    for(int i = 0; i < height; i++) {
        memcpy(greyData + width*i, imageData + widthStep*i, width*sizeof(uchar));
    }

    vector<uchar *> picList;
    vector<int> widthList;
    vector<int> heightList;
    CharExtracter::divideChar(greyData, width, height, picList, widthList, heightList);

    ushort res;
    wcout.imbue(locale("zh_CN.UTF-8"));
    for(int i = 0; i < picList.size(); i++) {
        Debug::showImageData(picList.at(i), widthList.at(i), heightList.at(i));
        res = OCR::recognize(picList.at(i), widthList.at(i), heightList.at(i));

        wcout << (wchar_t)res << endl;
    }

    delete []greyData;
    cvReleaseImage(&img);
    for(int i = 0; i < picList.size(); i++) {
        delete []picList.at(i);
    }
}

int main(int argc, char** argv) {
    OCR::preRecognize();

    ConfigFile *cfg = ConfigFile::parseConfig("config/debug.cfg");
    int from = cfg->getInt("from");
    int to = cfg->getInt("to");
    string dir = cfg->getString("imgdir");
    string postfix = cfg->getString("postfix");
    delete cfg;

    stringstream ss;
    //Timer start;
    for(int i = from; i <= to; i++) {
        ss.str("");
        ss << dir << i << postfix;

        func(ss.str().c_str());
    }

    //Timer end;
    //cout << Timer::calcTimeUsed(start, end) << "ms used" << endl;

    return (EXIT_SUCCESS);
}

#elif TRAIN == 1

// test MPLSH
int main(int argc, char** argv) {
    ifstream fin("data");
    int size, dim;
    fin >> size;
    fin >> dim;

    SampleSpace ss(dim, Distance::EUCLIDEAN);
    vector<Sample *> sampleList;

    ss.newSampleArray(sampleList, size);
    Sample *s;
    int label;
    float data;
    for(int i = 0; i < size; i++) {
        s = sampleList.at(i);
        fin >> label;
        s->setClass(label);
        for(int j = 0; j < dim; j++) {
            fin >> data;
            s->assignFeature(j, data);
        }
    }

    int M = 3;
    int L = 1;

    float **a = new float*[L];
    for(int i = 0; i < L; i++) {
        a[i] = new float[M*dim];
    }
    float **b = new float*[L];
    for(int i = 0; i < L; i++) {
        b[i] = new float[M];
    }
    float W = 3;

    for(int i = 0; i < L; i++) {
        for (int j = 0; j < M; j++) {
            for (int k = 0; k < dim; k++) {
                fin >> *(a[i] + dim*j +k);
            }
        }
    }

    for(int i = 0; i < L; i++) {
        for (int j = 0; j < M; j++) {
            fin >> *(b[i] + j);
        }
    }

    s = ss.newSample();
    s->assignFeature(0, 0.5);
    s->assignFeature(1, 0.5);

    MPLSH mplsh(ss);
    mplsh.preTrain(M, L, a, b, W);
    mplsh.train(sampleList);

    Variant v;
    v.m_varType = VT_USHORT;
    v.m_val.iVal = 4;
    mplsh.preClassify(v);
    mplsh.classify(*s);

    cout << s->getClass() << endl;

    mplsh.store2File("haha");

    MPLSH m2(ss);
    m2.loadFromFile("haha");

    m2.preClassify(v);
    m2.classify(*s);

    cout << s->getClass() << endl;
    
    for(int i = 0; i < L; i++) {
        delete[] a[i];
    }
    delete[] a;
    for(int i = 0; i < L; i++) {
        delete[] b[i];
    }
    delete[] b;

    ss.deleteSample(&s);
    ss.deleteSampleArray(sampleList);

    return (EXIT_SUCCESS);
}

#elif TRAIN == 0

void func(const char *filename) {
    IplImage *img = cvLoadImage(filename, 0);
    if(img == NULL) {
        cout << "cannot find file: " << filename << endl;
        return;
    }

    uchar *imageData = (uchar *)(img->imageData);
    int width = img->width;
    int height = img->height;
    int widthStep = img->widthStep;

    uchar *greyData = new uchar[width*height];
    for(int i = 0; i < height; i++) {
        memcpy(greyData + width*i, imageData + widthStep*i, width*sizeof(uchar));
    }

    vector<ushort> result;
    SDRS::retrieve(result, greyData, width, height);

    locale::global(locale("zh_CN.utf8"));
    int len = result.size();
    for (int i = 0; i < len; i++) {
        printf("%C", (wchar_t)result.at(i));
    }
    printf("\n");
    
    delete []greyData;
    cvReleaseImage(&img);
}

int main(int argc, char** argv) {
    SDRS::init(0);

    ConfigFile *cfg = ConfigFile::parseConfig("config/debug.cfg");
    int from = cfg->getInt("from");
    int to = cfg->getInt("to");
    string dir = cfg->getString("imgdir");
    string postfix = cfg->getString("postfix");
    delete cfg;
    
    stringstream ss;
    for(int i = from; i <= to; i++) {
        ss.str("");
        ss << dir << i << postfix;

        func(ss.str().c_str());
    }
    
    return (EXIT_SUCCESS);
}

#endif

#endif
